package com.zim.access.infrastructurre.generate;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.google.common.base.CaseFormat;
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.google.common.io.Resources;
import com.google.common.reflect.ClassPath;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.var;
import org.mvel2.templates.TemplateCompiler;
import org.mvel2.templates.TemplateRuntime;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author hanyue
 * @version 1.0
 * @date 2021/12/29 下午12:53
 */
public class CreateSql {

    // 实体类所在包
    private static final String PACKAGE_NAME = "com.zim.access.domain";

    // 文件所在位置
    private static final String MVEL_FILE_LOCATION = "mvel/createSql.yaml";

    private static final Map<Class, String> FILED_MAPPING = new ConcurrentHashMap<>();

    public static void main(String[] args) throws IOException {

        // 扫描所有类
        ClassPath classPath = ClassPath.from(Thread.currentThread().getContextClassLoader());

        // 扫描实体所在类
        for (ClassPath.ClassInfo classInfo : classPath.getTopLevelClassesRecursive(PACKAGE_NAME)) {
            try {
                Class<?> aClass = Class.forName(classInfo.getName());

                if (aClass.isAnnotationPresent(TableName.class)) {
                    Map<String, Object> values = new HashMap<>();

                    TableName tableName = aClass.getAnnotation(TableName.class);
                    values.put("tableName", tableName.value());

                    List<Item> items = new ArrayList<>();
                    values.put("items", items);

                    for (Field field : aClass.getDeclaredFields()) {
                        field.setAccessible(true);

                        if (field.isAnnotationPresent(TableId.class)) {
                            Id id = new Id();
                            id.setKey(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()));
                            id.setType(FILED_MAPPING.get(field.getType()));
                            values.put("id", id);
                        } else {
                            Item item = new Item();
                            item.setKey(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()));
                            item.setType(FILED_MAPPING.get(field.getType()));
                            items.add(item);
                        }
                    }

                    String sql = createSql(values);

                    System.out.println(sql);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    private static String createSql(Map<String, Object> values) throws IOException {
        URL url = Resources.getResource(MVEL_FILE_LOCATION);
        InputStream inputStream = url.openStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charsets.UTF_8);
        String body = CharStreams.toString(inputStreamReader);
        inputStream.close();
        inputStreamReader.close();

        var compiledTemplate = TemplateCompiler.compileTemplate(body);

        return (String) TemplateRuntime.execute(compiledTemplate, values);
    }

    static {
        FILED_MAPPING.put(int.class, "INTEGER");
        FILED_MAPPING.put(Integer.class, "INTEGER");

        FILED_MAPPING.put(long.class, "BIGINT");
        FILED_MAPPING.put(Long.class, "BIGINT");

        FILED_MAPPING.put(short.class, "SMALLINT");
        FILED_MAPPING.put(Short.class, "SMALLINT");

        FILED_MAPPING.put(float.class, "FLOAT");
        FILED_MAPPING.put(Float.class, "FLOAT");

        FILED_MAPPING.put(double.class, "DOUBLE");
        FILED_MAPPING.put(Double.class, "DOUBLE");

        FILED_MAPPING.put(BigDecimal.class, "NUMERIC");

        FILED_MAPPING.put(Byte.class, "TINYINT");
        FILED_MAPPING.put(byte.class, "TINYINT");

        FILED_MAPPING.put(Boolean.class, "BIT");
        FILED_MAPPING.put(boolean.class, "BIT");

        FILED_MAPPING.put(String.class, "varchar(255)");

        FILED_MAPPING.put(Date.class, "DATE");

        FILED_MAPPING.put(Timestamp.class, "TIMESTAMP");
        FILED_MAPPING.put(Calendar.class, "TIMESTAMP");

        FILED_MAPPING.put(byte[].class, "BLOB");
    }

    @Data
    @Accessors(chain = true)
    public static class Id {
        private String key;

        private String type;
    }

    @Data
    @Accessors(chain = true)
    public static class Item {
        private String key;

        private String type;
    }
}
